home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / Mail / collect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-14  |  12.4 KB  |  602 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. static char sccsid[] = "@(#)collect.c    5.17 (Berkeley) 1/16/89";
  20. #endif /* not lint */
  21.  
  22. /*
  23.  * Mail -- a mail program
  24.  *
  25.  * Collect input from standard input, handling
  26.  * ~ escapes.
  27.  */
  28.  
  29. #include "rcv.h"
  30. #include <sys/stat.h>
  31.  
  32. /*
  33.  * Read a message from standard output and return a read file to it
  34.  * or NULL on error.
  35.  */
  36.  
  37. /*
  38.  * The following hokiness with global variables is so that on
  39.  * receipt of an interrupt signal, the partial message can be salted
  40.  * away on dead.letter.
  41.  */
  42.  
  43. static    int    (*saveint)();        /* Previous SIGINT value */
  44. static    int    (*savehup)();        /* Previous SIGHUP value */
  45. static    int    (*savetstp)();        /* Previous SIGTSTP value */
  46. static    int    (*savettou)();        /* Previous SIGTTOU value */
  47. static    int    (*savettin)();        /* Previous SIGTTIN value */
  48. static    FILE    *collf;            /* File for saving away */
  49. static    int    hadintr;        /* Have seen one SIGINT so far */
  50.  
  51. static    jmp_buf    colljmp;        /* To get back to work */
  52. static    int    colljmp_p;        /* whether to long jump */
  53. static    jmp_buf    collabort;        /* To end collection with error */
  54.  
  55. FILE *
  56. collect(hp, printheaders)
  57.     struct header *hp;
  58. {
  59.     FILE *fbuf;
  60.     int lc, cc, escape, eofcount;
  61.     int collint(), collhup(), collstop();
  62.     register int c, t;
  63.     char linebuf[LINESIZE], *cp;
  64.     extern char tempMail[];
  65.     char getsub;
  66.     int omask;
  67.  
  68.     collf = NULL;
  69.     /*
  70.      * Start catching signals from here, but we're still die on interrupts
  71.      * until we're in the main loop.
  72.      */
  73.     omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
  74.     if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
  75.         signal(SIGINT, collint);
  76.     if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
  77.         signal(SIGHUP, collhup);
  78.     savetstp = signal(SIGTSTP, collstop);
  79.     savettou = signal(SIGTTOU, collstop);
  80.     savettin = signal(SIGTTIN, collstop);
  81.     if (setjmp(collabort) || setjmp(colljmp)) {
  82.         remove(tempMail);
  83.         goto err;
  84.     }
  85.     sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
  86.  
  87.     noreset++;
  88.     if ((collf = fopen(tempMail, "w+")) == NULL) {
  89.         perror(tempMail);
  90.         goto err;
  91.     }
  92.     unlink(tempMail);
  93.  
  94.     /*
  95.      * If we are going to prompt for a subject,
  96.      * refrain from printing a newline after
  97.      * the headers (since some people mind).
  98.      */
  99.     t = GTO|GSUBJECT|GCC|GNL;
  100.     getsub = 0;
  101.     if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
  102.         (value("ask") != NOSTR || value("asksub") != NOSTR))
  103.         t &= ~GNL, getsub++;
  104.     if (printheaders) {
  105.         puthead(hp, stdout, t);
  106.         fflush(stdout);
  107.     }
  108.     if ((cp = value("escape")) != NOSTR)
  109.         escape = *cp;
  110.     else
  111.         escape = ESCAPE;
  112.     eofcount = 0;
  113.     hadintr = 0;
  114.  
  115.     if (!setjmp(colljmp)) {
  116.         if (getsub)
  117.             grabh(hp, GSUBJECT);
  118.     } else {
  119.         /*
  120.          * Come here for printing the after-signal message.
  121.          * Duplicate messages won't be printed because
  122.          * the write is aborted if we get a SIGTTOU.
  123.          */
  124. cont:
  125.         if (hadintr) {
  126.             fflush(stdout);
  127.             fprintf(stderr,
  128.             "\n(Interrupt -- one more to kill letter)\n");
  129.         } else {
  130.             printf("(continue)\n");
  131.             fflush(stdout);
  132.         }
  133.     }
  134.     for (;;) {
  135.         colljmp_p = 1;
  136.         c = readline(stdin, linebuf, LINESIZE);
  137.         colljmp_p = 0;
  138.         if (c < 0) {
  139.             if (value("interactive") != NOSTR &&
  140.                 value("ignoreeof") != NOSTR && ++eofcount < 25) {
  141.                 printf("Use \".\" to terminate letter\n");
  142.                 continue;
  143.             }
  144.             break;
  145.         }
  146.         eofcount = 0;
  147.         hadintr = 0;
  148.         if (linebuf[0] == '.' && linebuf[1] == '\0' &&
  149.             value("interactive") != NOSTR &&
  150.             (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
  151.             break;
  152.         if (linebuf[0] != escape || value("interactive") == NOSTR) {
  153.             if (putline(collf, linebuf) < 0)
  154.                 goto err;
  155.             continue;
  156.         }
  157.         c = linebuf[1];
  158.         switch (c) {
  159.         default:
  160.             /*
  161.              * On double escape, just send the single one.
  162.              * Otherwise, it's an error.
  163.              */
  164.             if (c == escape) {
  165.                 if (putline(collf, &linebuf[1]) < 0)
  166.                     goto err;
  167.                 else
  168.                     break;
  169.             }
  170.             printf("Unknown tilde escape.\n");
  171.             break;
  172.         case 'C':
  173.             /*
  174.              * Dump core.
  175.              */
  176.             core();
  177.             break;
  178.         case '!':
  179.             /*
  180.              * Shell escape, send the balance of the
  181.              * line to sh -c.
  182.              */
  183.             shell(&linebuf[2]);
  184.             break;
  185.         case ':':
  186.             /*
  187.              * Escape to command mode, but be nice!
  188.              */
  189.             execute(&linebuf[2], 1);
  190.             goto cont;
  191.         case '.':
  192.             /*
  193.              * Simulate end of file on input.
  194.              */
  195.             goto out;
  196.         case 'q':
  197.             /*
  198.              * Force a quit of sending mail.
  199.              * Act like an interrupt happened.
  200.              */
  201.             hadintr++;
  202.             collint(SIGINT);
  203.             exit(1);
  204.         case 'h':
  205.             /*
  206.              * Grab a bunch of headers.
  207.              */
  208.             grabh(hp, GTO|GSUBJECT|GCC|GBCC);
  209.             goto cont;
  210.         case 't':
  211.             /*
  212.              * Add to the To list.
  213.              */
  214.             hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
  215.             break;
  216.         case 's':
  217.             /*
  218.              * Set the Subject list.
  219.              */
  220.             cp = &linebuf[2];
  221.             while (isspace(*cp))
  222.                 cp++;
  223.             hp->h_subject = savestr(cp);
  224.             break;
  225.         case 'c':
  226.             /*
  227.              * Add to the CC list.
  228.              */
  229.             hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
  230.             break;
  231.         case 'b':
  232.             /*
  233.              * Add stuff to blind carbon copies list.
  234.              */
  235.             hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
  236.             break;
  237.         case 'd':
  238.             strcpy(linebuf + 2, getdeadletter());
  239.             /* fall into . . . */
  240.         case 'r':
  241.             /*
  242.              * Invoke a file:
  243.              * Search for the file name,
  244.              * then open it and copy the contents to collf.
  245.              */
  246.             cp = &linebuf[2];
  247.             while (isspace(*cp))
  248.                 cp++;
  249.             if (*cp == '\0') {
  250.                 printf("Interpolate what file?\n");
  251.                 break;
  252.             }
  253.             cp = expand(cp);
  254.             if (cp == NOSTR)
  255.                 break;
  256.             if (isdir(cp)) {
  257.                 printf("%s: Directory\n", cp);
  258.                 break;
  259.             }
  260.             if ((fbuf = fopen(cp, "r")) == NULL) {
  261.                 perror(cp);
  262.                 break;
  263.             }
  264.             printf("\"%s\" ", cp);
  265.             fflush(stdout);
  266.             lc = 0;
  267.             cc = 0;
  268.             while (readline(fbuf, linebuf, LINESIZE) >= 0) {
  269.                 lc++;
  270.                 if ((t = putline(collf, linebuf)) < 0) {
  271.                     fclose(fbuf);
  272.                     goto err;
  273.                 }
  274.                 cc += t;
  275.             }
  276.             fclose(fbuf);
  277.             printf("%d/%d\n", lc, cc);
  278.             break;
  279.         case 'w':
  280.             /*
  281.              * Write the message on a file.
  282.              */
  283.             cp = &linebuf[2];
  284.             while (*cp == ' ' || *cp == '\t')
  285.                 cp++;
  286.             if (*cp == '\0') {
  287.                 fprintf(stderr, "Write what file!?\n");
  288.                 break;
  289.             }
  290.             if ((cp = expand(cp)) == NOSTR)
  291.                 break;
  292.             rewind(collf);
  293.             exwrite(cp, collf, 1);
  294.             break;
  295.         case 'm':
  296.         case 'M':
  297.         case 'f':
  298.         case 'F':
  299.             /*
  300.              * Interpolate the named messages, if we
  301.              * are in receiving mail mode.  Does the
  302.              * standard list processing garbage.
  303.              * If ~f is given, we don't shift over.
  304.              */
  305.             if (forward(linebuf + 2, collf, c) < 0)
  306.                 goto err;
  307.             goto cont;
  308.         case '?':
  309.             if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
  310.                 perror(THELPFILE);
  311.                 break;
  312.             }
  313.             while ((t = getc(fbuf)) != EOF)
  314.                 (void) putchar(t);
  315.             fclose(fbuf);
  316.             break;
  317.         case 'p':
  318.             /*
  319.              * Print out the current state of the
  320.              * message without altering anything.
  321.              */
  322.             rewind(collf);
  323.             printf("-------\nMessage contains:\n");
  324.             puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
  325.             while ((t = getc(collf)) != EOF)
  326.                 (void) putchar(t);
  327.             goto cont;
  328.         case '|':
  329.             /*
  330.              * Pipe message through command.
  331.              * Collect output as new message.
  332.              */
  333.             rewind(collf);
  334.             mespipe(collf, &linebuf[2]);
  335.             goto cont;
  336.         case 'v':
  337.         case 'e':
  338.             /*
  339.              * Edit the current message.
  340.              * 'e' means to use EDITOR
  341.              * 'v' means to use VISUAL
  342.              */
  343.             rewind(collf);
  344.             mesedit(collf, c);
  345.             goto cont;
  346.         }
  347.     }
  348.     goto out;
  349. err:
  350.     if (collf != NULL) {
  351.         fclose(collf);
  352.         collf = NULL;
  353.     }
  354. out:
  355.     if (collf != NULL)
  356.         rewind(collf);
  357.     noreset--;
  358.     sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
  359.     signal(SIGINT, saveint);
  360.     signal(SIGHUP, savehup);
  361.     signal(SIGTSTP, savetstp);
  362.     signal(SIGTTOU, savettou);
  363.     signal(SIGTTIN, savettin);
  364.     sigsetmask(omask);
  365.     return collf;
  366. }
  367.  
  368. /*
  369.  * Write a file, ex-like if f set.
  370.  */
  371.  
  372. exwrite(name, fp, f)
  373.     char name[];
  374.     FILE *fp;
  375. {
  376.     register FILE *of;
  377.     register int c;
  378.     long cc;
  379.     int lc;
  380.     struct stat junk;
  381.  
  382.     if (f) {
  383.         printf("\"%s\" ", name);
  384.         fflush(stdout);
  385.     }
  386.     if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
  387.         if (!f)
  388.             fprintf(stderr, "%s: ", name);
  389.         fprintf(stderr, "File exists\n");
  390.         return(-1);
  391.     }
  392.     if ((of = fopen(name, "w")) == NULL) {
  393.         perror(NOSTR);
  394.         return(-1);
  395.     }
  396.     lc = 0;
  397.     cc = 0;
  398.     while ((c = getc(fp)) != EOF) {
  399.         cc++;
  400.         if (c == '\n')
  401.             lc++;
  402.         (void) putc(c, of);
  403.         if (ferror(of)) {
  404.             perror(name);
  405.             fclose(of);
  406.             return(-1);
  407.         }
  408.     }
  409.     fclose(of);
  410.     printf("%d/%ld\n", lc, cc);
  411.     fflush(stdout);
  412.     return(0);
  413. }
  414.  
  415. /*
  416.  * Edit the message being collected on fp.
  417.  * On return, make the edit file the new temp file.
  418.  */
  419. mesedit(fp, c)
  420.     FILE *fp;
  421. {
  422.     int (*sigint)() = signal(SIGINT, SIG_IGN);
  423.     FILE *nf = run_editor(fp, (off_t)-1, c, 0);
  424.  
  425.     if (nf != NULL) {
  426.         fseek(nf, (off_t)0, 2);
  427.         collf = nf;
  428.         fclose(fp);
  429.     }
  430.     (void) signal(SIGINT, sigint);
  431. }
  432.  
  433. /*
  434.  * Pipe the message through the command.
  435.  * Old message is on stdin of command;
  436.  * New message collected from stdout.
  437.  * Sh -c must return 0 to accept the new message.
  438.  */
  439. mespipe(fp, cmd)
  440.     FILE *fp;
  441.     char cmd[];
  442. {
  443.     FILE *nf;
  444.     int (*sigint)() = signal(SIGINT, SIG_IGN);
  445.     extern char tempEdit[];
  446.  
  447.     if ((nf = fopen(tempEdit, "w+")) == NULL) {
  448.         perror(tempEdit);
  449.         goto out;
  450.     }
  451.     (void) unlink(tempEdit);
  452.     /*
  453.      * stdin = current message.
  454.      * stdout = new message.
  455.      */
  456.     if (run_command(cmd, 0, fileno(fp), fileno(nf), NOSTR) < 0) {
  457.         (void) fclose(nf);
  458.         goto out;
  459.     }
  460.     if (fsize(nf) == 0) {
  461.         fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
  462.         (void) fclose(nf);
  463.         goto out;
  464.     }
  465.     /*
  466.      * Take new files.
  467.      */
  468.     (void) fseek(nf, 0L, 2);
  469.     collf = nf;
  470.     (void) fclose(fp);
  471. out:
  472.     (void) signal(SIGINT, sigint);
  473. }
  474.  
  475. /*
  476.  * Interpolate the named messages into the current
  477.  * message, preceding each line with a tab.
  478.  * Return a count of the number of characters now in
  479.  * the message, or -1 if an error is encountered writing
  480.  * the message temporary.  The flag argument is 'm' if we
  481.  * should shift over and 'f' if not.
  482.  */
  483. forward(ms, fp, f)
  484.     char ms[];
  485.     FILE *fp;
  486. {
  487.     register int *msgvec;
  488.     extern char tempMail[];
  489.     struct ignoretab *ig;
  490.     char *tabst;
  491.  
  492.     msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
  493.     if (msgvec == (int *) NOSTR)
  494.         return(0);
  495.     if (getmsglist(ms, msgvec, 0) < 0)
  496.         return(0);
  497.     if (*msgvec == 0) {
  498.         *msgvec = first(0, MMNORM);
  499.         if (*msgvec == NULL) {
  500.             printf("No appropriate messages\n");
  501.             return(0);
  502.         }
  503.         msgvec[1] = NULL;
  504.     }
  505.     if (f == 'f' || f == 'F')
  506.         tabst = NOSTR;
  507.     else if ((tabst = value("tabstr")) == NOSTR)
  508.         tabst = "\t";
  509.     ig = isupper(f) ? NULL : ignore;
  510.     printf("Interpolating:");
  511.     for (; *msgvec != 0; msgvec++) {
  512.         struct message *mp = message + *msgvec - 1;
  513.  
  514.         touch(mp);
  515.         printf(" %d", *msgvec);
  516.         if (send(mp, fp, ig, tabst) < 0) {
  517.             perror(tempMail);
  518.             return(-1);
  519.         }
  520.     }
  521.     printf("\n");
  522.     return(0);
  523. }
  524.  
  525. /*
  526.  * Print (continue) when continued after ^Z.
  527.  */
  528. /*ARGSUSED*/
  529. collstop(s)
  530. {
  531.     int (*old_action)() = signal(s, SIG_DFL);
  532.  
  533.     sigsetmask(sigblock(0) & ~sigmask(s));
  534.     kill(0, s);
  535.     sigblock(sigmask(s));
  536.     signal(s, old_action);
  537.     if (colljmp_p) {
  538.         colljmp_p = 0;
  539.         hadintr = 0;
  540.         longjmp(colljmp, 1);
  541.     }
  542. }
  543.  
  544. /*
  545.  * On interrupt, come here to save the partial message in ~/dead.letter.
  546.  * Then jump out of the collection loop.
  547.  */
  548. /*ARGSUSED*/
  549. collint(s)
  550. {
  551.     /*
  552.      * the control flow is subtle, because we can be called from ~q.
  553.      */
  554.     if (!hadintr) {
  555.         if (value("ignore") != NOSTR) {
  556.             puts("@");
  557.             fflush(stdout);
  558.             clearerr(stdin);
  559.             return;
  560.         }
  561.         hadintr = 1;
  562.         longjmp(colljmp, 1);
  563.     }
  564.     rewind(collf);
  565.     if (value("nosave") == NOSTR)
  566.         savedeadletter(collf);
  567.     longjmp(collabort, 1);
  568. }
  569.  
  570. /*ARGSUSED*/
  571. collhup(s)
  572. {
  573.     rewind(collf);
  574.     savedeadletter(collf);
  575.     /*
  576.      * Let's pretend nobody else wants to clean up,
  577.      * a true statement at this time.
  578.      */
  579.     exit(1);
  580. }
  581.  
  582. savedeadletter(fp)
  583.     register FILE *fp;
  584. {
  585.     register FILE *dbuf;
  586.     register int c;
  587.     char *cp;
  588.  
  589.     if (fsize(fp) == 0)
  590.         return;
  591.     cp = getdeadletter();
  592.     c = umask(077);
  593.     dbuf = fopen(cp, "a");
  594.     (void) umask(c);
  595.     if (dbuf == NULL)
  596.         return;
  597.     while ((c = getc(fp)) != EOF)
  598.         (void) putc(c, dbuf);
  599.     fclose(dbuf);
  600.     rewind(fp);
  601. }
  602.